use crate::{
    state::State,
    types::{BcError, ErrorCode, Notify, Stats},
};
use axum::{
    async_trait,
    extract::ws::{Message, WebSocket},
};
use futures::{
    stream::{SplitSink, SplitStream},
    SinkExt, StreamExt,
};
use nanoid::nanoid;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, net::SocketAddr, sync::Arc};
use tokio::sync::Mutex;

#[derive(Debug)]
pub struct EventHandlerCtx {
    pub state: Arc<State>,
    pub client: Arc<Mutex<Client>>,
    pub message: Option<Event>,
}

#[async_trait]
pub trait EventHandler: std::fmt::Debug + 'static + Send + Sync {
    fn get_event(&self) -> EventType;
    async fn process_event(&mut self, ctx: EventHandlerCtx) -> Result<Event, BcError>;
}

#[derive(Debug, Serialize, Deserialize, Clone, Copy, Eq, PartialEq, Hash)]
#[serde(rename_all = "camelCase")]
pub enum EventName {
    Auth,
    Notify,
    Welcome,
    Stats,
    Error,
}

#[derive(Debug, Serialize, Deserialize, Clone, Copy, Eq, PartialEq, Hash)]
pub enum EventType {
    Event(EventName),
    Connect,
    Disconnect,
}

#[derive(Debug)]
pub struct Client {
    pub id: String,
    pub recv: SplitStream<WebSocket>,
    pub send: SplitSink<WebSocket, Message>,
    pub addr: SocketAddr,
    pub user_agent: String,
    pub bot_id: Option<String>,
}

impl Client {
    pub async fn send(&mut self, event: Event) {
        let res = self
            .send
            .send(Message::Text(serde_json::to_string(&event).unwrap()))
            .await;
        if let Err(e) = res {
            log::error!("error sending message: {e}");
        }
    }

    pub async fn interrupt_with_error(&mut self, err: BcError) {
        let st = serde_json::to_string(&err).unwrap();
        let res = self.send.send(Message::Text(st)).await;
        if let Err(e) = res {
            log::error!("error closing errorneous connection: {e}");
        }
        if let Err(e) = self.send.send(Message::Close(None)).await {
            log::error!("error closing errorneous connection: {e}");
        }
    }
}

#[derive(Debug)]
pub struct Emitter {
    clients: HashMap<String, Arc<Mutex<Client>>>,
    handlers: Vec<(EventType, Arc<Mutex<dyn EventHandler>>)>,
}

#[derive(Debug)]
pub enum Audience {
    Fanout,
    Connection(String),
    Affected(String),
}

#[derive(Debug, thiserror::Error)]
pub enum EmitterError {
    #[error("serialization error - `{0}`")]
    Serialization(serde_json::Error),
    #[error("send error - `{0}`")]
    Send(axum::Error),
    #[error("{0:?}")]
    Sends(Vec<axum::Error>),
}

impl Emitter {
    pub fn add_handler(&mut self, handler: impl EventHandler) {
        self.handlers
            .push((handler.get_event(), Arc::new(Mutex::new(handler))));
    }

    pub fn get_handlers_for(&self, event: EventType) -> Vec<Arc<Mutex<dyn EventHandler>>> {
        self.handlers
            .iter()
            .filter(|x| x.0 == event)
            .map(|e| Arc::clone(&e.1))
            .collect()
    }

    pub async fn get_audience(&self, audience: Audience) -> Vec<Arc<Mutex<Client>>> {
        match audience {
            Audience::Fanout => self.clients.iter().map(|x| Arc::clone(x.1)).collect(),
            Audience::Connection(id) => self
                .clients
                .iter()
                .filter(|x| x.0.eq(&id))
                .map(|x| Arc::clone(x.1))
                .collect(),
            Audience::Affected(id) => self
                .clients
                .iter()
                .filter(|x| {
                    if let Some(s) = &x.1.blocking_lock().bot_id {
                        s.eq(&id)
                    } else {
                        false
                    }
                })
                .map(|x| Arc::clone(x.1))
                .collect(),
        }
    }

    pub async fn emit<T: Serialize>(
        &self,
        event: Event,
        audience: Audience,
    ) -> Result<(), EmitterError> {
        let audience = self.get_audience(audience).await;
        let data = serde_json::to_string(&event).map_err(EmitterError::Serialization)?;

        let mut errs = vec![];
        for client in audience {
            if let Err(e) = client
                .lock()
                .await
                .send
                .send(Message::Text(data.clone()))
                .await
            {
                errs.push(e);
            }
        }

        if errs.len() > 0 {
            Err(EmitterError::Sends(errs))
        } else {
            Ok(())
        }
    }

    pub fn new() -> Self {
        Self {
            clients: HashMap::new(),
            handlers: Vec::new(),
        }
    }

    async fn recv_job(&self, id: &String, state: Arc<State>) {
        let client = self.clients.get(id).unwrap().clone();
        let connect_handlers = self.get_handlers_for(EventType::Connect);

        for handler in connect_handlers {
            let res = handler
                .lock()
                .await
                .process_event(EventHandlerCtx {
                    state: state.clone(),
                    client: client.clone(),
                    message: None,
                })
                .await;
            if let Err(e) = res {
                client.lock().await.interrupt_with_error(e).await;
                return;
            }
        }

        while let Some(Ok(msg)) = client.lock().await.recv.next().await {
            if let Message::Text(msg) = msg {
                let data = serde_json::from_str::<Event>(&msg);
                let data = match data {
                    Ok(e) => e,
                    Err(_) => {
                        client
                            .lock()
                            .await
                            .interrupt_with_error(BcError {
                                code: ErrorCode::BadRequest,
                                message: "bad request".into(),
                            })
                            .await;
                        break;
                    }
                };
                let typ: EventType = data.clone().into();
                let handlers = self.get_handlers_for(typ);

                for handler in handlers {
                    let mut handler = handler.lock().await;
                    let res = handler
                        .process_event(EventHandlerCtx {
                            state: state.clone(),
                            client: client.clone(),
                            message: Some(data.clone()),
                        })
                        .await;
                    match res {
                        Ok(e) => {
                            client.lock().await.send(e).await;
                        }
                        Err(e) => {
                            client.lock().await.interrupt_with_error(e).await;
                        }
                    };
                }
            }
        }
    }

    pub async fn track(
        &mut self,
        socket: WebSocket,
        addr: SocketAddr,
        user_agent: String,
        state: Arc<State>,
    ) {
        let id: String = nanoid!();
        let (send, recv) = socket.split();
        let client = Client {
            id: id.clone(),
            send,
            recv,
            addr,
            bot_id: None,
            user_agent,
        };

        self.clients
            .insert(id.clone(), Arc::new(Mutex::new(client)));

        self.recv_job(&id, state).await;
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "event", content = "data", rename_all = "camelCase")]
pub enum Event {
    Auth { token: String },
    Error(BcError),
    Notify(Notify),
    Stats(Stats),
    Welcome { id: String },
}

impl Into<EventType> for Event {
    fn into(self) -> EventType {
        EventType::Event(self.into())
    }
}

impl Into<EventName> for Event {
    fn into(self) -> EventName {
        match self {
            Event::Auth { .. } => EventName::Auth,
            Event::Stats(_) => EventName::Stats,
            Event::Error(_) => EventName::Error,
            Event::Notify(_) => EventName::Notify,
            Event::Welcome { .. } => EventName::Welcome,
        }
    }
}
